/*
* json is a C++ wrapper for jansson.
*
* @author: dzlua
* @contact: 505544956@qq.com
* @date: September 30, 2020
* @example: https://gitee.com/dzlua/sqlite.git
*/

#ifndef __SQLITE_HPP_
#define __SQLITE_HPP_

#include <sqlite3.h>
#include <functional>
#include <string.h>

namespace sqlite {
   
// Warpper a C style class to c++ class.
// https://gitee.com/dzlua/test/blob/master/WrapperC/wrapper.hpp
template <typename T, typename C>
class Wrapper {
  public:
    Wrapper(C *cptr = nullptr) noexcept { reset(cptr); }
    //Wrapper(const Wrapper<T,C>& r) noexcept { reset(r.get()); }
    //Wrapper<T,C>& operator=(const Wrapper<T,C>& r) noexcept { reset(r.get()); return *this; }
    //Wrapper<T,C>& operator=(C *cptr) noexcept { reset(cptr); return *this; }
  public:
    inline T* operator->() noexcept { return &obj_; }
    inline operator C*() const noexcept { return get(); }
    inline explicit operator bool() const noexcept { return get() != nullptr; }
  public:
    inline C* get() const noexcept { return obj_._get(); }
    inline C*& set() noexcept { return obj_._set(); }
    inline void reset(C *cptr = nullptr) noexcept { obj_._reset(cptr); }
    inline void swap(Wrapper<T,C>& r) noexcept { obj_._swap(r.obj_); }
  protected:
    T obj_;
};

template <typename T, typename C>
inline bool operator==(const Wrapper<T,C>& l, const Wrapper<T,C>& r) noexcept { return l.get() == r.get(); }
template <typename T, typename C>
inline bool operator==(const Wrapper<T,C>& l, std::nullptr_t) noexcept { return l.get() == nullptr; }
template <typename T, typename C>
inline bool operator==(std::nullptr_t, const Wrapper<T,C>& r) noexcept { return nullptr == r.get(); }
template <typename T, typename C>
inline bool operator!=(const Wrapper<T,C>& l, const Wrapper<T,C>& r) noexcept { return l.get() != r.get(); }
template <typename T, typename C>
inline bool operator!=(const Wrapper<T,C>& l, std::nullptr_t) noexcept { return l.get() != nullptr; }
template <typename T, typename C>
inline bool operator!=(std::nullptr_t, const Wrapper<T,C>& r) noexcept { return nullptr != r.get(); }

template <typename T, typename C>
class ClassWrapped {
    friend class Wrapper<T,C>;
  protected:
    ClassWrapped() : cptr_(nullptr) {}
    virtual ~ClassWrapped() { _reset(nullptr); }
    inline C* _get() const noexcept { return cptr_; }
    inline C*& _set() noexcept { return cptr_; }
    inline void _reset(C *cptr = nullptr) noexcept { cptr_ = cptr; }
    inline void _swap(ClassWrapped<T,C>& r) noexcept { std::swap(cptr_, r.cptr_); }
  protected:
    C* cptr_;
};

// macros for wrapper sqlite.
#define WRAPPER_CLASS_BEGIN(classname,jsonstruct) \
  class classname : public ClassWrapped<classname, jsonstruct> { \
      friend class Wrapper<classname,jsonstruct>; \
    protected: \
      classname() = default; \
    public: \
      typedef Wrapper<classname,jsonstruct> Wrapped;
#define WRAPPER_CLASS_END };

typedef int result;
inline bool isOK(result ret) { return ret == SQLITE_OK; }
inline bool isDone(result ret) { return ret == SQLITE_DONE; }

typedef sqlite3_int64 int64;
typedef sqlite3_uint64 uint64;
typedef sqlite3_callback Callback;
typedef int(*BusyHandler)(void*,int);
typedef int (*AuthHandler)(void*,int,const char*,const char*,const char*,const char*);
typedef void(*TraceHandler)(void*,const char*);
typedef int(*TraceHandlerV2)(unsigned,void*,void*,void*);
typedef void(*ProfileHandler)(void*,const char*,uint64);
typedef int(*ProgressHandler)(void*);
typedef void(*DeleteHandler)(void*);

enum class ValueType : int {
  Integer = SQLITE_INTEGER,
  Float = SQLITE_FLOAT,
  Text = SQLITE3_TEXT,
  Blob = SQLITE_BLOB,
  Null = SQLITE_NULL
};

enum class TextRep : int {
  UTF8 = SQLITE_UTF8,
  UTF16LE = SQLITE_UTF16LE,
  UTF16BE = SQLITE_UTF16BE,
  UTF16 = SQLITE_UTF16,
  UTF16Aligned = SQLITE_UTF16_ALIGNED
};

namespace detail {
  WRAPPER_CLASS_BEGIN(Table, char*)
    inline void reset(char **zResult, int row, int col, result code) {
      cptr_ = zResult; row_ = row; col_ = col; code_ = code; }
    inline int row() const { return row_; }
    inline int col() const { return col_; }
    inline char* data(int row, int col) { return cptr_[row*col_+col]; }
    inline void each(std::function<bool(int row, int col, char *data)> callback) {
      for (int i = 0; i < row_; ++i) {
        for (int j = 0; j < col_; ++j) {
          if (!callback(i, j, cptr_[i*col_+j]))
            return;
        }
      }
    }
    inline void free() { sqlite3_free_table(cptr_); }
    inline result errcode() const { return code_; }
    inline bool isOK() const { return code_ == SQLITE_OK; }
    
    protected:
      int row_, col_;
      result code_;
  WRAPPER_CLASS_END

  WRAPPER_CLASS_BEGIN(Mutex, sqlite3_mutex)
    static inline Wrapped alloc(int id) { return Wrapped(sqlite3_mutex_alloc(id)); }
    inline void free() { sqlite3_mutex_free(cptr_); }
    inline void enter() { sqlite3_mutex_enter(cptr_); }
    inline result retry() { return sqlite3_mutex_try(cptr_); }
    inline void leave() { sqlite3_mutex_leave(cptr_); }
  WRAPPER_CLASS_END

  WRAPPER_CLASS_BEGIN(Value, sqlite3_value)
    inline const void* blobValue() { return sqlite3_value_blob(cptr_); }
    inline int bytesValue() { return sqlite3_value_bytes(cptr_); }
    inline int bytes16Value() { return sqlite3_value_bytes16(cptr_); }
    inline double doubleValue() { return sqlite3_value_double(cptr_); }
    inline int intValue() { return sqlite3_value_int(cptr_); }
    inline int64 int64Value() { return sqlite3_value_int64(cptr_); }
    inline void* pointerValue(const char *zPType) { return sqlite3_value_pointer(cptr_, zPType); }
    inline const unsigned char* textValue() { return sqlite3_value_text(cptr_); }
    inline const void* text16Value() { return sqlite3_value_text16(cptr_); }
    inline const void* text16leValue() { return sqlite3_value_text16le(cptr_); }
    inline const void* text16beValue() { return sqlite3_value_text16be(cptr_); }
    inline ValueType type() { return (ValueType)sqlite3_value_type(cptr_); }
    inline ValueType numericType() { return (ValueType)sqlite3_value_numeric_type(cptr_); }
    inline bool nochange() { return sqlite3_value_nochange(cptr_); }
    inline bool frombind() { return sqlite3_value_frombind(cptr_); }
    inline unsigned int subtype() { return sqlite3_value_subtype(cptr_); }
    inline Wrapped valueDup() { return Wrapped(sqlite3_value_dup(cptr_)); }
    inline void valueFree() { sqlite3_value_free(cptr_); }
  WRAPPER_CLASS_END

  WRAPPER_CLASS_BEGIN(Stmt, sqlite3_stmt)
    inline const char* sql() { return sqlite3_sql(cptr_); }
    inline char* expandedSql() { return sqlite3_expanded_sql(cptr_); }
    inline const char* normalizedSql() { return sqlite3_normalized_sql(cptr_); }
    inline bool readonly() { return sqlite3_stmt_readonly(cptr_); }
    inline bool isExplain() { return sqlite3_stmt_isexplain(cptr_); }
    inline bool busy() { return sqlite3_stmt_busy(cptr_); }

    // bind functions
    inline result bindBlob(const void *data, int len, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_blob(cptr_, index, data, len, nullptr); }
    inline result bindBlob(const void *data, int len, DeleteHandler funDel, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_blob(cptr_, index, data, len, funDel); }
    inline result bindBlob64(const void *data, uint64 len, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_blob64(cptr_, index, data, len, nullptr); }
    inline result bindBlob(const void *data, uint64 len, DeleteHandler funDel, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_blob64(cptr_, index, data, len, funDel); }
    inline result bindDouble(double value, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_double(cptr_, index, value); }
    inline result bindInt(int value, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_int(cptr_, index, value); }
    inline result bindInt64(int64 value, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_int64(cptr_, index, value); }
    inline result bindNull(int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_null(cptr_, index); }
    inline result bindText(const char *data, int len, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_text(cptr_, index, data, len, nullptr); }
    inline result bindText(const char *data, int len, DeleteHandler funDel, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_text(cptr_, index, data, len, funDel); }
    inline result bindText16(const void *data, int len, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_text16(cptr_, index, data, len, nullptr); }
    inline result bindText16(const void *data, int len, DeleteHandler funDel, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_text16(cptr_, index, data, len, funDel); }
    inline result bindText64(const char *data, uint64 len, unsigned char encoding, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_text64(cptr_, index, data, len, nullptr, encoding); }
    inline result bindText64(const char *data, uint64 len, unsigned char encoding, DeleteHandler funDel, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_text64(cptr_, index, data, len, funDel, encoding); }
    inline result bindValue(const sqlite3_value *value, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_value(cptr_, index, value); }
    inline result bindZeroBlob(int len, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_zeroblob(cptr_, index, len); }
    inline result bindZeroBlob64(uint64 len, int index = -1) {
      if (index == -1) index = ++bindIndex_;
      return sqlite3_bind_zeroblob64(cptr_, index, len); }
    inline int bindParameterCount() { return sqlite3_bind_parameter_count(cptr_); }
    inline const char* bindParameterName(int index) { return sqlite3_bind_parameter_name(cptr_, index); }
    inline int bindParameterIndex(const char *name) { return sqlite3_bind_parameter_index(cptr_, name); }
    inline result bindClear() { auto rc = sqlite3_clear_bindings(cptr_); if (SQLITE_OK == rc) bindClearIndex(); return rc; }
    inline void bindClearIndex() { bindIndex_ = 0; }

    // column do not change index
    inline int columnCount() { return sqlite3_column_count(cptr_); }
    inline const char* columnName(int index = -1) {
      if (index == -1) index = columnIndex_;
      return sqlite3_column_name(cptr_, index); }
    inline const void* columnName16(int index = -1) {
      if (index == -1) index = columnIndex_;
      return sqlite3_column_name16(cptr_, index); }
    inline const char* columnDatabaseName(int index = -1) {
      if (index == -1) index = columnIndex_;
      return sqlite3_column_database_name(cptr_, index); }
    inline const void* columnDatabaseName16(int index = -1) {
      if (index == -1) index = columnIndex_;
      return sqlite3_column_database_name16(cptr_, index); }
    inline const char* columnTableName(int index = -1) {
      if (index == -1) index = columnIndex_;
      return sqlite3_column_table_name(cptr_, index); }
    inline const void* columnTableName16(int index = -1) {
      if (index == -1) index = columnIndex_;
      return sqlite3_column_table_name16(cptr_, index); }
    inline const char* columnOriginName(int index = -1) {
      if (index == -1) index = columnIndex_;
      return sqlite3_column_origin_name(cptr_, index); }
    inline const void* columnOriginName16(int index = -1) {
      if (index == -1) index = columnIndex_;
      return sqlite3_column_origin_name16(cptr_, index); }
    inline const char* columnDeclType(int index = -1) {
      if (index == -1) index = columnIndex_;
      return sqlite3_column_decltype(cptr_, index); }
    inline const void* columnDeclType16(int index = -1) {
      if (index == -1) index = columnIndex_;
      return sqlite3_column_decltype16(cptr_, index); }
    inline void columnClearIndex() { columnIndex_ = 0; }

    // column change index
    inline int dataCount() { return sqlite3_data_count(cptr_); }
    inline const void* columnBlob(int index = -1) {
      if (index == -1) index = columnIndex_++;
      return sqlite3_column_blob(cptr_, index); }
    inline int columnBytes(int index = -1) {
      if (index == -1) index = columnIndex_++;
      return sqlite3_column_bytes(cptr_, index); }
    inline int columnBytes16(int index = -1) {
      if (index == -1) index = columnIndex_++;
      return sqlite3_column_bytes16(cptr_, index); }
    inline double columnDouble(int index = -1) {
      if (index == -1) index = columnIndex_++;
      return sqlite3_column_double(cptr_, index); }
    inline int columnInt(int index = -1) {
      if (index == -1) index = columnIndex_++;
      return sqlite3_column_int(cptr_, index); }
    inline int64 columnInt64(int index = -1) {
      if (index == -1) index = columnIndex_++;
      return sqlite3_column_int64(cptr_, index); }
    inline const unsigned char* columnText(int index = -1) {
      if (index == -1) index = columnIndex_++;
      return sqlite3_column_text(cptr_, index); }
    inline const void* columnText16(int index = -1) {
      if (index == -1) index = columnIndex_++;
      return sqlite3_column_text16(cptr_, index); }
    inline ValueType columnType(int index = -1) {
      if (index == -1) index = columnIndex_++;
      return (ValueType)sqlite3_column_type(cptr_, index); }
    inline Value::Wrapped columnValue(int index = -1) {
      if (index == -1) index = columnIndex_++;
      return Value::Wrapped(sqlite3_column_value(cptr_, index)); }

    inline result step() { return sqlite3_step(cptr_); }
    inline result finalize() { return sqlite3_finalize(cptr_); }
    inline result reset() { auto rc = sqlite3_reset(cptr_); if (SQLITE_OK == rc) reset(cptr_, code_); return rc; }

    inline void reset(sqlite3_stmt *stmt, result code) {
      cptr_ = stmt; code_ = code; bindClearIndex(); columnClearIndex(); }
    inline result errcode() const { return code_; }
    inline bool isOK() const { return code_ == SQLITE_OK; }

    protected:
      result code_;
      int bindIndex_;
      int columnIndex_;
  WRAPPER_CLASS_END

  WRAPPER_CLASS_BEGIN(Context, sqlite3_context)
  WRAPPER_CLASS_END

  WRAPPER_CLASS_BEGIN(Sqlite, sqlite3)
    inline result open(const char *filename) {
      return sqlite3_open(filename, &cptr_); }
    inline result open16(const char *filename) {
      return sqlite3_open16(filename, &cptr_); }
    inline result openV2(const char *filename, int flags, const char *zVfs) {
      return sqlite3_open_v2(filename, &cptr_, flags, zVfs); }
    inline result close() { return sqlite3_close(cptr_); }
    inline result closeV2() { return sqlite3_close_v2(cptr_); }
    inline result exec(const char *sql, Callback callback, void *udata = nullptr, char **errmsg = nullptr) {
      return sqlite3_exec(cptr_, sql, callback, udata, errmsg); }

    template <typename ...Args>
    inline result dbConfig(int op, Args ... args) {
      return sqlite3_config(op, args...); }

    inline result extendedResultCodes(bool on) { sqlite3_extended_result_codes(cptr_, on); }

    inline int64 lastInsertRowid() { return sqlite3_last_insert_rowid(cptr_); }
    inline void setLastInsertRowid(int64 id) { return sqlite3_set_last_insert_rowid(cptr_, id); }

    inline int changes() { return sqlite3_changes(cptr_); }
    inline int totalChanges() { return sqlite3_total_changes(cptr_); }
    inline void interrupt() { sqlite3_interrupt(cptr_); }

    inline result complete(const char *sql) { return sqlite3_complete(sql); }
    inline result complete16(const char *sql) { return sqlite3_complete16(sql); }

    inline result setBusyHandler(BusyHandler handler, void *udata) {
      return sqlite3_busy_handler(cptr_, handler, udata); }
    inline result setBusyTimeout(int ms) { return sqlite3_busy_timeout(cptr_, ms); }
    inline Table::Wrapped getTable(const char *zSql, char **pzErrmsg = nullptr) {
      char **zResult = nullptr;
      int row, col;
      auto ret = sqlite3_get_table(cptr_, zSql, &zResult, &row, &col, pzErrmsg);
      Table::Wrapped table;
      table->reset(zResult, row, col, ret);
      return std::move(table);
    }

    inline result setAuthorizer(AuthHandler handler, void *udata) {
      return sqlite3_set_authorizer(cptr_, handler, udata); }
    inline void* setTrace(TraceHandler handler, void *udata) {
      return sqlite3_trace(cptr_, handler, udata); }
    inline void* setProfile(ProfileHandler handler, void *udata) {
      return sqlite3_profile(cptr_, handler, udata); }
    inline int setTraceV2(unsigned uMask, TraceHandlerV2 handler, void *udata) {
      return sqlite3_trace_v2(cptr_, uMask, handler, udata); }
    inline void setProgressHandler(int nOps, ProgressHandler handler, void *udata) {
      sqlite3_progress_handler(cptr_, nOps, handler, udata); }

    inline int limit(int id, int newVal) { return sqlite3_limit(cptr_, id, newVal); }
    
    inline result errcode() const { return sqlite3_errcode(cptr_); }
    inline result extendedErrcode() const { return sqlite3_extended_errcode(cptr_); }
    inline const char* errmsg() const { return sqlite3_errmsg(cptr_); }
    inline const void* errmsg16() const { return sqlite3_errmsg16(cptr_); }

    inline bool isOK() const { return errcode() == SQLITE_OK; }

    inline Stmt::Wrapped prepare(const char *zSql, int nByte = -1, const char **pzTail = nullptr) {
      Stmt::Wrapped stmt;
      if (nByte < 0) nByte = strlen(zSql);
      auto ret = sqlite3_prepare(cptr_, zSql, nByte, &stmt.set(), pzTail);
      stmt->reset(stmt.get(), ret);
      return std::move(stmt);
    }
    inline Stmt::Wrapped prepareV2(const char *zSql, int nByte = -1, const char **pzTail = nullptr) {
      Stmt::Wrapped stmt;
      if (nByte < 0) nByte = strlen(zSql);
      auto ret = sqlite3_prepare_v2(cptr_, zSql, nByte, &stmt.set(), pzTail);
      stmt->reset(stmt.get(), ret);
      return std::move(stmt);
    }
    inline Stmt::Wrapped prepareV3(const char *zSql, int nByte, unsigned int prepFlags, const char **pzTail = nullptr) {
      Stmt::Wrapped stmt;
      if (nByte < 0) nByte = strlen(zSql);
      auto ret = sqlite3_prepare_v3(cptr_, zSql, nByte, prepFlags, &stmt.set(), pzTail);
      stmt->reset(stmt.get(), ret);
      return std::move(stmt);
    }
    inline Stmt::Wrapped prepare16(const void *zSql, int nByte, const void **pzTail = nullptr) {
      Stmt::Wrapped stmt;
      auto ret = sqlite3_prepare16(cptr_, zSql, nByte, &stmt.set(), pzTail);
      stmt->reset(stmt.get(), ret);
      return std::move(stmt);
    }
    inline Stmt::Wrapped prepare16V2(const void *zSql, int nByte, const void **pzTail = nullptr) {
      Stmt::Wrapped stmt;
      auto ret = sqlite3_prepare16_v2(cptr_, zSql, nByte, &stmt.set(), pzTail);
      stmt->reset(stmt.get(), ret);
      return std::move(stmt);
    }
    inline Stmt::Wrapped prepare16V3(const void *zSql, int nByte, unsigned int prepFlags, const void **pzTail = nullptr) {
      Stmt::Wrapped stmt;
      auto ret = sqlite3_prepare16_v3(cptr_, zSql, nByte, prepFlags, &stmt.set(), pzTail);
      stmt->reset(stmt.get(), ret);
      return std::move(stmt);
    }

    inline result createFunction(const char *zFunctionName,
        int nArg, TextRep eTextRep, void *pApp,
        void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
        void (*xStep)(sqlite3_context*,int,sqlite3_value**),
        void (*xFinal)(sqlite3_context*)) {
      return sqlite3_create_function(cptr_, zFunctionName, nArg, (int)eTextRep, pApp, xFunc, xStep, xFinal); }
    inline result createFunction16(const void *zFunctionName,
        int nArg, TextRep eTextRep, void *pApp,
        void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
        void (*xStep)(sqlite3_context*,int,sqlite3_value**),
        void (*xFinal)(sqlite3_context*)) {
      return sqlite3_create_function16(cptr_, zFunctionName, nArg, (int)eTextRep, pApp, xFunc, xStep, xFinal); }
    inline result createFunctionV2(const char *zFunctionName,
        int nArg, TextRep eTextRep, void *pApp,
        void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
        void (*xStep)(sqlite3_context*,int,sqlite3_value**),
        void (*xFinal)(sqlite3_context*),
        void(*xDestroy)(void*)) {
      return sqlite3_create_function_v2(cptr_, zFunctionName, nArg, (int)eTextRep, pApp, xFunc, xStep, xFinal, xDestroy); }

    inline result createCollation(
        const char *zName,
        TextRep eTextRep,
        void *pArg,
        int(*xCompare)(void*,int,const void*,int,const void*)) {
      return sqlite3_create_collation(cptr_, zName, (int)eTextRep, pArg, xCompare); }
    inline result createCollationV2(
        const char *zName,
        TextRep eTextRep,
        void *pArg,
        int(*xCompare)(void*,int,const void*,int,const void*),
        void(*xDestroy)(void*)) {
      return sqlite3_create_collation_v2(cptr_, zName, (int)eTextRep, pArg, xCompare, xDestroy); }
    inline result createCollation16(
        const void *zName,
        TextRep eTextRep,
        void *pArg,
        int(*xCompare)(void*,int,const void*,int,const void*)) {
      return sqlite3_create_collation16(cptr_, zName, (int)eTextRep, pArg, xCompare); }

    inline int collationNeeded(void *pCollNeededArg, void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*)) {
      return sqlite3_collation_needed(cptr_, pCollNeededArg, xCollNeeded); }
    inline int collationNeeded16(void *pCollNeededArg, void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const void*)) {
      return sqlite3_collation_needed16(cptr_, pCollNeededArg, xCollNeeded); }

    inline Mutex::Wrapped dbMutex() { return Mutex::Wrapped(sqlite3_db_mutex(cptr_)); }
  WRAPPER_CLASS_END

  WRAPPER_CLASS_BEGIN(FileName, char)
    inline const char* database() {
      return sqlite3_filename_database(cptr_); }
    inline const char* journal() {
      return sqlite3_filename_journal(cptr_); }
    inline const char* wal() {
      return sqlite3_filename_wal(cptr_); }
    inline sqlite3_file* databaseObject() {  
      return sqlite3_database_file_object(cptr_); }

    inline const char* uriParameter(const char *zParam) {
      return sqlite3_uri_parameter(cptr_, zParam); }
    inline bool uriBoolean(const char *zParam, bool defValue) {
      return sqlite3_uri_boolean(cptr_, zParam, defValue); }
    inline int64 uriInt64(const char *zParam, int64 defValue) {
      return sqlite3_uri_int64(cptr_, zParam, defValue); }
    inline const char* uriKey(int N) {
      return sqlite3_uri_key(cptr_, N); }
  WRAPPER_CLASS_END
} // end namespace detail
typedef detail::Table::Wrapped Table;
typedef detail::Stmt::Wrapped Stmt;
typedef detail::Sqlite::Wrapped Sqlite;
typedef detail::FileName::Wrapped FileName;

inline Sqlite sqlite() { Sqlite db; return std::move(db); }

inline const char* version() { return sqlite3_version; }
inline const char* libversion() { return sqlite3_libversion(); }
inline const char* sourceid() { return sqlite3_sourceid(); }
inline int libversionNumber() { return sqlite3_libversion_number(); }

inline int compileoptionUsed(const char *zOptName) { return sqlite3_compileoption_used(zOptName); }
inline const char* compileoptionGet(int N) { return sqlite3_compileoption_get(N); }

inline bool threadsafe() { return sqlite3_threadsafe() != 0; }

inline result initialize() { return sqlite3_initialize(); }
inline result shutdown() { return sqlite3_shutdown(); }
inline result osInit() { return sqlite3_os_init(); }
inline result osEnd() { return sqlite3_os_end(); }

template <typename ...Args>
inline result config(int op, Args ... args) {
  return sqlite3_config(op, args...); }

template <typename ...Args>
inline char* mprintf(const char *fmt, Args ... args) {
  return sqlite3_mprintf(fmt, args...); }
inline char* vmprintf(const char *fmt, va_list va) {
  return sqlite3_vmprintf(fmt, va); }
template <typename ...Args>
inline char* snprintf(int n, char *buf, const char *fmt, Args ... args) {
  return sqlite3_snprintf(n, buf, fmt, args...); }
inline char* vsnprintf(int n, char *buf, const char *fmt, va_list va) {
  return sqlite3_vsnprintf(n, buf, fmt, va); }

inline void* malloc(int n) { return sqlite3_malloc(n); }
template <typename T>
inline T* malloc(int n) {
  return (T*)sqlite3_malloc(n); }
inline void* malloc64(uint64 n) { return sqlite3_malloc64(n); }
template <typename T>
inline T* malloc64(uint64 n) {
  return (T*)sqlite3_malloc64(n); }
inline void* realloc(void *d, int n) { return sqlite3_realloc(d, n); }
template <typename T>
inline T* realloc(void *d, int n) {
  return (T*)sqlite3_realloc(d, n); }
inline void* realloc64(void *d, uint64 n) { return sqlite3_realloc64(d, n); }
template <typename T>
inline T* realloc64(void *d, uint64 n) {
  return (T*)sqlite3_realloc64(d, n); }
inline void free(void *d) { sqlite3_free(d); }
inline uint64 msize(void *d) { return sqlite3_msize(d); }

inline int64 memoryUsed() { return sqlite3_memory_used(); }
inline int64 memoryHighwater(bool reset) { return sqlite3_memory_highwater(reset); }

inline void randomness(int N, void *P) { sqlite3_randomness(N, P); }

inline FileName createFilename(const char *zDatabase, const char *zJournal
    , const char *zWal, int nParam, const char **azParam ) {
  return FileName(sqlite3_create_filename(zDatabase, zJournal, zWal, nParam, azParam)); }

inline const char* errstr(result ret) { return sqlite3_errstr(ret); }
inline result sleep(int ms) { return sqlite3_sleep(ms); }
inline const char* tempDirectory() { return sqlite3_temp_directory; }
inline void setTempDirectory(char *d) { sqlite3_temp_directory = d; }
inline const char* dataDirectory() { return sqlite3_data_directory; }
inline void setDataDirectory(char *d) { sqlite3_data_directory = d; }

// sqlite3_win32_set_directory
// sqlite3_win32_set_directory8
// sqlite3_win32_set_directory16

} // end namespace sqlite

#endif // end __SQLITE_HPP_